package com.coocaa.ops.admin.config.redis;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import com.coocaa.ops.common.tool.base.InstanceUtil;
import com.coocaa.ops.common.tool.cache.compress.ZipCompressProvider;
import com.coocaa.ops.common.tool.cache.enums.CacheEnvEnums;
import com.coocaa.ops.common.tool.cache.impl.MyCacheServiceImpl;
import com.coocaa.ops.common.tool.cache.util.ZipUtil;
import com.coocaa.ops.common.tool.json.JsonUtil;
import io.lettuce.core.ReadFrom;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author bijiahao
 * @date : 2019/6/19.
 * @description redis配置加载
 */
@Slf4j
@Configuration
public class RedisConfig {
    @Autowired
    private RedisMasterSlaveProperties redisMasterSlaveProperties;
    @Autowired
    private RedisClusterProperties redisClusterProperties;

    @Primary
    @Bean("ClusterConfig")
    public RedisClusterConfiguration redisClusterConfiguration() {
        log.info("redisClusterProperties: " + JsonUtil.obj2Json(redisClusterProperties));
        RedisClusterConfiguration configuration = new RedisClusterConfiguration();
        String nodes = redisClusterProperties.getNodes();
        configuration.setClusterNodes(getNodes(nodes));
        configuration.setMaxRedirects(Integer.parseInt(redisClusterProperties.getMaxRedirects()));
        configuration.setPassword(redisClusterProperties.getPassword());
        return configuration;
    }

    @Bean("ClusterPoolConfig")
    public GenericObjectPoolConfig redisClusterPool() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(Integer.parseInt(redisClusterProperties.getLettucePoolMaxIdle()));
        config.setMinIdle(Integer.parseInt(redisClusterProperties.getLettucePoolMinIdle()));
        config.setMaxTotal(Integer.parseInt(redisClusterProperties.getLettucePoolMaxActive()));
        config.setMaxWaitMillis(Long.parseLong(redisClusterProperties.getLettucePoolMaxWait()));
        return config;
    }

    @Bean("ClusterFactory")
    public LettuceConnectionFactory lettuceConnectionFactoryForCluster(@Qualifier("ClusterConfig") RedisClusterConfiguration redisClusterConfiguration, @Qualifier("ClusterPoolConfig") GenericObjectPoolConfig poolConfig) {
        LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofMillis(Long.parseLong(redisClusterProperties.getTimeout())))
                .poolConfig(poolConfig)
                .build();
        return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
    }

    @Bean("RedisClusterTemplate")
    public RedisTemplate<String, Serializable> redisClusterTemplate(@Qualifier("ClusterFactory") LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate();
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        GenericFastJsonRedisSerializer valueSerializer = new GenericFastJsonRedisSerializer();
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        redisTemplate.setKeySerializer(keySerializer);
        // 数据被压缩，使用json会有问题
        redisTemplate.setValueSerializer(keySerializer);
        redisTemplate.setHashKeySerializer(keySerializer);
        redisTemplate.setHashValueSerializer(valueSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }


    @Bean("MasterSlaveConfig")
    public RedisStaticMasterReplicaConfiguration redisMasterSlaveConfiguration() {
        log.info("redisMasterSlaveProperties: " + JsonUtil.obj2Json(redisMasterSlaveProperties));
        RedisStaticMasterReplicaConfiguration configuration = new RedisStaticMasterReplicaConfiguration(getUrl(redisMasterSlaveProperties.getMaster()), getPort(redisMasterSlaveProperties.getMaster()));
        configuration.addNode(getUrl(redisMasterSlaveProperties.getSlave()), getPort(redisMasterSlaveProperties.getSlave()));
        configuration.setPassword(redisMasterSlaveProperties.getPassword());
        return configuration;
    }

    @Bean("MasterSlavePoolConfig")
    public GenericObjectPoolConfig redisMasterSlavePool() {
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(Integer.parseInt(redisMasterSlaveProperties.getLettucePoolMaxIdle()));
        config.setMinIdle(Integer.parseInt(redisMasterSlaveProperties.getLettucePoolMinIdle()));
        config.setMaxTotal(Integer.parseInt(redisMasterSlaveProperties.getLettucePoolMaxActive()));
        config.setMaxWaitMillis(Long.parseLong(redisMasterSlaveProperties.getLettucePoolMaxWait()));
        return config;
    }


    @Primary
    @Bean("MasterSlaveFactory")
    public LettuceConnectionFactory lettuceConnectionFactoryForMasterSlave(@Qualifier("MasterSlaveConfig") RedisStaticMasterReplicaConfiguration redisStaticMasterReplicaConfiguration, @Qualifier("MasterSlavePoolConfig") GenericObjectPoolConfig poolConfig) {
        LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofMillis(Long.parseLong(redisClusterProperties.getTimeout())))
                .poolConfig(poolConfig)
                .readFrom(ReadFrom.SLAVE_PREFERRED)
                .build();
        return new LettuceConnectionFactory(redisStaticMasterReplicaConfiguration, clientConfiguration);
    }

    @Primary
    @Bean("RedisMasterSlaveTemplate")
    public RedisTemplate<String, Serializable> redisMasterSlaveTemplate(@Qualifier("MasterSlaveFactory") LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate();
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        GenericFastJsonRedisSerializer valueSerializer = new GenericFastJsonRedisSerializer();
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        redisTemplate.setKeySerializer(keySerializer);
        // 数据被压缩，使用json会有问题
        redisTemplate.setValueSerializer(keySerializer);
        redisTemplate.setHashKeySerializer(keySerializer);
        redisTemplate.setHashValueSerializer(valueSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }


    @Bean
    public MyCacheServiceImpl myCacheService(@Qualifier("RedisClusterTemplate") RedisTemplate<String, Serializable> redisClusterTemplate,
                                             @Qualifier("RedisMasterSlaveTemplate") RedisTemplate<String, Serializable> redisMasterSlaveTemplate,
                                             @Qualifier("zipCompressProvider") ZipCompressProvider zipCompressProvider
    ) {
        Map<String, RedisTemplate<String, Serializable>> redisTemplateMap = InstanceUtil.newHashMap(3);
        redisTemplateMap.put(CacheEnvEnums.REDIS_MASTER_SLAVE.getEnv(), redisMasterSlaveTemplate);
        redisTemplateMap.put(CacheEnvEnums.REDIS_CLUSTER.getEnv(), redisClusterTemplate);
        MyCacheServiceImpl myCacheService = new MyCacheServiceImpl(redisTemplateMap, zipCompressProvider);
        //先执行下,可以加快redis初始响应
        redisMasterSlaveTemplate.opsForValue().get("V1");
        redisClusterTemplate.opsForValue().get("V1");
        return myCacheService;
    }

    @Bean
    public ZipCompressProvider zipCompressProvider() {
        ZipCompressProvider zipCompressProvider = new ZipCompressProvider();
        return zipCompressProvider;
    }

    public static List<RedisNode> getNodes(String hosts) {
        if (StringUtils.isBlank(hosts)) {
            return Collections.emptyList();
        }
        String[] hostList = hosts.split(",");
        List<RedisNode> redisNodes = new ArrayList<>(hostList.length);
        for (String host : hostList) {
            String url = getUrl(host);
            Integer port = getPort(host);
            redisNodes.add(new RedisNode(url, port));
        }
        return redisNodes;
    }

    private static String getUrl(String host) {
        return host.split(":")[0];
    }

    private static Integer getPort(String host) {
        return Integer.parseInt(host.split(":")[1]);
    }

}
